/**
 * README
 * 
 * Modificacion de la biblioteca jWave disponible en http://code.google.com/p/jwave/
 * Convertidos los tipos de datos a float para agilizar el tratamiendo de grandes arrays de datos
 * como son los que se tratan en analisis genomicos.
 * Los autores originales de las funciones y del código de la biblioteca cuando fue seleccionada en
 * Abril de 2012 eran:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Por supuesto, todo el merito, honores y demas menciones sobre esta adaptacion les corresponden a ellos
 * y nada mas que a ellos.
 * 
 * 
 * This is a modified version of jWave, available in http://code.google.com/p/jwave/
 * It has been replace the double datatype with float for performance reasons in very big arrays used in
 * bioinformatics like genomic analysis.
 * Original authors of functions and the library when it has been selected in 2012's April were:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Of course, all the merits and mentions about this adaptation have corresponded to them.
 * 
 */

package ev.maths.handlers;

import ev.maths.handlers.wavelets.WaveletInterface;

/**
 * Clase para realizar la transformada wavelet por paquetes
 * 
 * @date 01.08.2012
 * @author Enrique Vázquez de Luis
 */
public class WaveletPacketTransform extends WaveletTransform {

   /**
   * Constructor, recibe un objeto wavelet
   *
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param wavelet La wavelet madre que usará
   */
  public WaveletPacketTransform( WaveletInterface wavelet ) {
    super(wavelet);
  } // WaveletPacketTransform
  
  /**
   * Constructor que recibe la wavelet madre y las iteraciones a realizar
   *
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param wavelet
   */
  public WaveletPacketTransform( WaveletInterface wavelet, int iteration ) {
    super(wavelet, iteration);
  } // WaveletPacketTransform

  /**
   * Realiza la transformada unidimensional de wavelet por paquetes
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  @Override
  public float[ ] forwardWavelet( float[ ] arrTime ) {

    float[ ] arrHilb = new float[ arrTime.length ];
    for( int i = 0; i < arrTime.length; i++ )
      arrHilb[ i ] = arrTime[ i ];

    int k = arrTime.length;
    int h = arrTime.length;
    int minWaveLength = _wavelet.getWaveLength( );
    if( h >= minWaveLength ) {

      while( h >= minWaveLength ) {

        int g = k / h; // 1 -> 2 -> 4 -> 8 -> ...

        for( int p = 0; p < g; p++ ) {

          float[ ] iBuf = new float[ h ];

          for( int i = 0; i < h; i++ )
            iBuf[ i ] = arrHilb[ i + ( p * h ) ];

          float[ ] oBuf = _wavelet.forward( iBuf );

          for( int i = 0; i < h; i++ )
            arrHilb[ i + ( p * h ) ] = oBuf[ i ];

        } // packets

        h = h >> 1;

       // level++;

      } // levels

    } // if

    return arrHilb;
  } // forward

  /**
   * Realiza la transformada inversa de wavelet por paquetes
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes del dominio de Hilbert
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  @Override
  public float[ ] reverseWavelet( float[ ] arrHilb ) {

    float[ ] arrTime = new float[ arrHilb.length ];

    for( int i = 0; i < arrHilb.length; i++ )
      arrTime[ i ] = arrHilb[ i ];

   // int level = 0;
    int minWaveLength = _wavelet.getWaveLength( );
    int k = arrTime.length;
    int h = minWaveLength;
    if( arrHilb.length >= minWaveLength ) {

      while( h <= arrTime.length && h >= minWaveLength ) {

        int g = k / h;

        for( int p = 0; p < g; p++ ) {

          float[ ] iBuf = new float[ h ];

          for( int i = 0; i < h; i++ )
            iBuf[ i ] = arrTime[ i + ( p * h ) ];

          float[ ] oBuf = _wavelet.reverse( iBuf );

          for( int i = 0; i < h; i++ )
            arrTime[ i + ( p * h ) ] = oBuf[ i ];

        } // packets

        h = h << 1;

      //  level++;

      } // levels

    } // if

    return arrTime;
  } // reverse

  /**
   * Realiza la transformada discreta de wavelet hasta un nivel
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @param toLevel nivel hasta el que realizar la transformada
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[], int)
   */
  @Override
  public float[ ] forwardWavelet( float[ ] arrTime, int toLevel ) {

    float[ ] arrHilb = new float[ arrTime.length ];
    for( int i = 0; i < arrTime.length; i++ )
      arrHilb[ i ] = arrTime[ i ];

    int level = 0;
    int k = arrTime.length;
    int h = arrTime.length;
    int minWaveLength = _wavelet.getWaveLength( );
    if( h >= minWaveLength ) {

      while( h >= minWaveLength && level < toLevel ) {

        int g = k / h; 

        for( int p = 0; p < g; p++ ) {

          float[ ] iBuf = new float[ h ];

          for( int i = 0; i < h; i++ )
            iBuf[ i ] = arrHilb[ i + ( p * h ) ];

          float[ ] oBuf = _wavelet.forward( iBuf );

          for( int i = 0; i < h; i++ )
            arrHilb[ i + ( p * h ) ] = oBuf[ i ];

        } // packets

        h = h >> 1;

        level++;

      } // levels

    } // if

    return arrHilb;
  } // forward

  /**
   * Realiza la transformada inversa discreta de wavelet desde un nivel determinado
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes de Hilbert
   * @param fromLevel iteraciones a realizar
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[], int)
   */
  @Override
  public float[ ] reverseWavelet( float[ ] arrHilb, int fromLevel ) {

    float[ ] arrTime = new float[ arrHilb.length ];

    for( int i = 0; i < arrHilb.length; i++ )
      arrTime[ i ] = arrHilb[ i ];

    int level = 0;
    
    int minWaveLength = _wavelet.getWaveLength( );
    
    int k = arrTime.length;
    
    int h = (int) (arrHilb.length / (Math.pow(2, fromLevel-1))); 
    
    if( arrHilb.length >= minWaveLength ) {

      while( h <= arrTime.length && h >= minWaveLength && level < fromLevel ) {

        int g = k / h; // ... -> 8 -> 4 -> 2 -> 1

        for( int p = 0; p < g; p++ ) {

          float[ ] iBuf = new float[ h ];

          for( int i = 0; i < h; i++ )
            iBuf[ i ] = arrTime[ i + ( p * h ) ];

          float[ ] oBuf = _wavelet.reverse( iBuf );

          for( int i = 0; i < h; i++ )
            arrTime[ i + ( p * h ) ] = oBuf[ i ];

        } // packets

        h = h << 1;

        level++;

      } // levels

    } // if

    return arrTime;
  } // reverse

} // class
